#!/usr/bin/env python
# Standard imports
from __future__ import print_function
from __future__ import division
# Third-Party imports
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
# Custom imports
import Options

from Vector import *
from Color import *
from Tools import *
from GLWrappers import *


class Perspective(object):
    def __init__(self, FOVY, zNear, zFar, resolution):
        self.FOVY = FOVY
        self.zNear = zNear
        self.zFar = zFar
        self.SetSize(*resolution)

    def SetSize(self, width, height):
        self.width, self.height = width, height
        if self.width <= 0:
            self.width = 1
        if self.height <= 0:
            self.height = 1
        self.aspectRatio = self.width // self.height

    def Apply(self):
        glViewport(0, 0, self.width, self.height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        self.Draw()
        glMatrixMode(GL_MODELVIEW)

    def Draw(self):
        gluPerspective(self)


class Camera():
    def __init__(self, position, focus, up):
        self.position = Vector(*position)
        self.focus = Vector(*focus)
        self.up = Vector(*up)
        self._defaultPosition = self.position
        self._defaultFocus = self.focus
        self._defaultUp = self.up
        self.moveSpeed = 0.1

    def update(self, dt, KeyStatus):
        if KeyStatus["Left"] and not KeyStatus["Right"]:
            self.position = AddDeltaScalar(self.position, self.left, dt, self.moveSpeed)
            if not KeyStatus["LShift"] and not KeyStatus["RShift"]:
                self.focus = AddDeltaScalar(self.focus, self.left, dt, self.moveSpeed)
        elif KeyStatus["Right"] and not KeyStatus["Left"]:
            self.position = AddDeltaScalar(self.position, self.right, dt, self.moveSpeed)
            if not KeyStatus["LShift"] and not KeyStatus["RShift"]:
                self.focus = AddDeltaScalar(self.focus, self.right, dt, self.moveSpeed)
        if KeyStatus["Down"] and not KeyStatus["Up"]:
            if KeyStatus["LCtrl"] or KeyStatus["RCtrl"]:
                self.position = AddDeltaScalar(self.position, self.down, dt, self.moveSpeed)
                if not KeyStatus["LShift"] and not KeyStatus["RShift"]:
                    self.focus = AddDeltaScalar(self.focus, self.down, dt, self.moveSpeed)
            else:
                self.position = AddDeltaScalar(self.position, self.forward.Negate(), dt, self.moveSpeed)
                self.focus = AddDeltaScalar(self.focus, self.forward.Negate(), dt, self.moveSpeed)

        elif KeyStatus["Up"] and not KeyStatus["Down"]:
            if KeyStatus["LCtrl"] or KeyStatus["RCtrl"]:
                self.position = AddDeltaScalar(self.position, self.up, dt, self.moveSpeed)
                if not KeyStatus["LShift"] and not KeyStatus["RShift"]:
                    self.focus = AddDeltaScalar(self.focus, self.up, dt, self.moveSpeed)
            else:
                self.position = AddDeltaScalar(self.position, self.forward, dt, self.moveSpeed)
                self.focus = AddDeltaScalar(self.focus, self.forward, dt, self.moveSpeed)


    def Draw(self):
        gluLookAt(self)

    def Reset(self):
        self.position = self._defaultPosition
        self.focus = self._defaultFocus
        self.up = self._defaultUp

    @property
    def x(self):
        return self.position[0]

    @x.setter
    def x(self, value):
        self.position[0] = value

    @property
    def y(self):
        return self.position[1]

    @y.setter
    def y(self, value):
        self.position[1] = value

    @property
    def z(self):
        return self.position[2]

    @z.setter
    def z(self, value):
        self.position[2] = value

    @property
    def forward(self):
        return (self.focus - self.position).Normalize()

    @property
    def right(self):
        return Cross(self.forward, self.up).Normalize()

    @property
    def left(self):
        return Cross(self.up, self.forward).Normalize()

    @property
    def down(self):
        return self.up.Negate()


class Window(object):
    def __init__(self, perspective, camera, resolution):
        pygame.init()
        pygame.display.set_mode(resolution, pygame.DOUBLEBUF|pygame.OPENGL|pygame.HWSURFACE)
        pygame.display.set_caption("The Deep")

        self.perspective = perspective
        self.camera = camera
        class Angle(object):
            pass
        self.angle = Angle()
        self.angle.x = self.angle.y = self.angle.z = 0.0
        class Mouse(object):
            pass
        self.mouse = Mouse()
        self.mouse.x = self.mouse.prev_x = 30
        self.mouse.y = self.mouse.prev_y = 30
        self.mouse.buttons = [0, 0, 0] # left, middle, right buttons
        self.size = None
        self.KeyStatus = {"Left":False, "Right":False, "Up":False, "Down":False,"LShift":False,"RShift":False,"LAlt":False,"RAlt":False,"LCtrl":False,"RCtrl":False,}
        self.timer = pygame.time.Clock()
        self.dt = 0
        self.InitGL()

    def Loop(self):
        """Simple looping mechanism for the game."""
        self.dt = self.timer.tick()
        self.DoAllEvents()
        glLoadIdentity()
        self.camera.update(self.dt, self.KeyStatus)
        self.ClearBuffers()
        self.camera.Draw()
        self.OnDraw()
        self.SwapBuffers()

    def DoAllEvents(self):
        """Handle all the events on the queue."""
        for event in pygame.event.get():
            self.ProcessEvent(event)

    def DoEvent(self):
        """Handle only ONE of the events on the queue."""
        event = pygame.event.poll()
        if event != pygame.NOEVENT:
            self.ProcessEvent(event)

    def ProcessEvent(self, event):
        """Low level processing of a singular event."""
        if event.type == pygame.QUIT:
            self.OnQuit()
        elif event.type == pygame.VIDEORESIZE:
            self.OnSize(event.w, event.h)
        elif event.type == pygame.KEYDOWN:
            self.OnKeyDown(event)
        elif event.type == pygame.KEYUP:
            self.OnKeyUp(event)
        elif event.type == pygame.MOUSEBUTTONDOWN:
            self.OnMouseDown(event)
        elif event.type == pygame.MOUSEBUTTONUP:
            self.OnMouseUp(event)
        elif event.type == pygame.MOUSEMOTION:
            self.OnMouseMotion(event)

    def OnKeyUp(self, event):
        if event.key == pygame.K_ESCAPE:
            self.OnQuit()
        elif event.key == pygame.K_LEFT:
            self.KeyStatus["Left"] = False
        elif event.key == pygame.K_RIGHT:
            self.KeyStatus["Right"] = False
        elif event.key == pygame.K_UP:
            self.KeyStatus["Up"] = False
        elif event.key == pygame.K_DOWN:
            self.KeyStatus["Down"] = False
        elif event.key == pygame.K_RSHIFT:
            self.KeyStatus["RShift"] = False
        elif event.key == pygame.K_LSHIFT:
            self.KeyStatus["LShift"] = False
        elif event.key == pygame.K_RALT:
            self.KeyStatus["RAlt"] = False
        elif event.key == pygame.K_LALT:
            self.KeyStatus["LAlt"] = False
        elif event.key == pygame.K_RCTRL:
            self.KeyStatus["RCtrl"] = False
        elif event.key == pygame.K_LCTRL:
            self.KeyStatus["LCtrl"] = False


    def OnKeyDown(self, event):
        if event.key == pygame.K_LEFT:
            self.KeyStatus["Left"] = True
        elif event.key == pygame.K_RIGHT:
            self.KeyStatus["Right"] = True
        elif event.key == pygame.K_UP:
            self.KeyStatus["Up"] = True
        elif event.key == pygame.K_DOWN:
            self.KeyStatus["Down"] = True
        elif event.key == pygame.K_RSHIFT:
            self.KeyStatus["RShift"] = True
        elif event.key == pygame.K_LSHIFT:
            self.KeyStatus["LShift"] = True
        elif event.key == pygame.K_RALT:
            self.KeyStatus["RAlt"] = True
        elif event.key == pygame.K_LALT:
            self.KeyStatus["LAlt"] = True
        elif event.key == pygame.K_RCTRL:
            self.KeyStatus["RCtrl"] = True
        elif event.key == pygame.K_LCTRL:
            self.KeyStatus["LCtrl"] = True

    def InitGL(self):
        """Stub for derived classes. Must be called manually."""
        raise NotImplementedError("You must define a custom InitGL function.")

    def ClearBuffers(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    def SwapBuffers(self):
        pygame.display.flip()

    def OnQuit(self):
        #if Options.AreDebugging:
            #print('fps:',str(int(Clock.get_fps())))
        pygame.quit()
        exit()

    def OnSize(self, width, height):
        self.width, self.height = width, height
        if self.width <= 0:
            self.width = 1
        if self.height <= 0:
            self.height = 1
        self.perspective.SetSize(self.width, self.height)
        self.perspective.Apply()
        #if Options.AreDebugging:
            #print("Window resized to", newWidth, "by", newHeight)
            #print("Aspect ratio is", self.perspective.aspectRatio)

    def OnDraw(self):
        """Stub for derived classes"""
        raise NotImplementedError("You must define a custom OnDraw function.")

    def OnMouseDown(self, event):
        self.mouse.buttons[event.button-1] = 1

    def OnMouseUp(self, event):
        self.mouse.buttons[event.button-1] = 0

    def OnMouseMotion(self, event):
        self.mouse.prev_x, self.mouse.prev_y = self.mouse.x, self.mouse.y
        self.mouse.x, self.mouse.y = event.pos
